package drds.plus.datasource.thread;

import com.alibaba.druid.pool.DruidDataSource;
import drds.plus.datasource.configuration.DatasourceConfiguration;
import drds.plus.datasource.configuration.DatasourceConfigurationParser;
import drds.plus.datasource.configuration.DatasourceHandler;
import drds.plus.datasource.connection_restrict.ConnectionRestrictEntry;
import lombok.extern.slf4j.Slf4j;

import java.util.List;
import java.util.Properties;

/**
 * 应用配置监听，当应用配置发生变化时，区分发生 变化的配置，来决定具体是flush还是reCreate
 */
@Slf4j
public class DatasourceChange {
    private DatasourceHandler datasourceHandler;

    public DatasourceChange(DatasourceHandler datasourceHandler) {
        this.datasourceHandler = datasourceHandler;
    }

    public void onDataRecieved(String dataId, String data) {
        if (null == data) {
            return;
        }
        datasourceHandler.lock.lock();
        try {
            String string = data;
            DatasourceConfiguration newDatasourceConfiguration = DatasourceConfigurationParser.parseDatasourceConfiguration(string);
            DatasourceConfiguration cloneDatasourceConfiguration = datasourceHandler.runTimeDatasourceConfiguration.clone();
            // 有些既有配置不能变更，所以克隆老的配置，然后将新的set进去
            cloneDatasourceConfiguration.setUsername(newDatasourceConfiguration.getUsername());
            cloneDatasourceConfiguration.setMinPoolSize(newDatasourceConfiguration.getMinPoolSize());
            cloneDatasourceConfiguration.setMaxPoolSize(newDatasourceConfiguration.getMaxPoolSize());
            cloneDatasourceConfiguration.setInitPoolSize(newDatasourceConfiguration.getInitPoolSize());
            cloneDatasourceConfiguration.setIdleTimeout(newDatasourceConfiguration.getIdleTimeout());
            cloneDatasourceConfiguration.setBlockingTimeout(newDatasourceConfiguration.getBlockingTimeout());
            cloneDatasourceConfiguration.setConnectionProperties(newDatasourceConfiguration.getConnectionProperties());

            // 增加3个具体的实现
            cloneDatasourceConfiguration.setWriteRestrictTimes(newDatasourceConfiguration.getWriteRestrictTimes());
            cloneDatasourceConfiguration.setReadRestrictTimes(newDatasourceConfiguration.getReadRestrictTimes());
            cloneDatasourceConfiguration.setThreadCountRestrict(newDatasourceConfiguration.getThreadCountRestrict());
            cloneDatasourceConfiguration.setTimeSliceInMillis(newDatasourceConfiguration.getTimeSliceInMillis());
            boolean isNeedReCreate = needReCreate(datasourceHandler.runTimeDatasourceConfiguration, cloneDatasourceConfiguration);
            if (isNeedReCreate) {
                // 转换tAtomDsConfDO
                DruidDataSource druidDataSource;
                try {
                    druidDataSource = DatasourceHandler.createDruidDataSource(datasourceHandler.datasourceId, cloneDatasourceConfiguration);
                } catch (Exception e1) {
                    log.error("[DRUID GlobaConfError] createDruidDataSource Error! dataId : " + dataId + " configuration : " + data);
                    return;
                }
                // 检查转换后结果是否正确
                if (!DatasourceHandler.checkDruidDataSource(druidDataSource)) {
                    log.error("[DRUID GlobaConfError] dataSource Prams Error! dataId : " + dataId + " configuration : " + data);
                    return;
                }
                try {
                    // 这个必须在最前面，否则下次推送可能无法比较出不同而不创建新数据
                    datasourceHandler.runTimeDatasourceConfiguration = cloneDatasourceConfiguration;
                    datasourceHandler.druidDataSource.close();
                    log.info("[DRUID destroy OldDataSource] dataId : " + dataId);
                    druidDataSource.init();
                    // druidDataSource.getDataSourceStat().setMaxSqlSize(DruidDsConfHandle.druidStatMaxKeySize);
                    log.info("[DRUID create newDataSource] dataId : " + dataId);
                    datasourceHandler.druidDataSource = druidDataSource;
                    datasourceHandler.clearDataSourceWrapper();
                } catch (Exception e) {
                    log.error("[DRUID Create GlobaConf Error]  Always ReCreate DataSource Error ! dataId: " + dataId, e);
                }
            } else {
                boolean isNeedFlush = changeNeedFlush(datasourceHandler.runTimeDatasourceConfiguration, cloneDatasourceConfiguration);
                /**
                 * 阀值变化无需刷新持有的数据源，只要更新runTimeConf，并且清空wrapDataSource
                 */
                boolean isRestrictChange = isRestrictChange(datasourceHandler.runTimeDatasourceConfiguration, cloneDatasourceConfiguration);
                if (isNeedFlush) {
                    try {
                        datasourceHandler.runTimeDatasourceConfiguration = cloneDatasourceConfiguration;
                        Properties properties = new Properties();
                        properties.putAll(cloneDatasourceConfiguration.getConnectionProperties());
                        datasourceHandler.druidDataSource.setConnectProperties(properties);
                        datasourceHandler.druidDataSource.setMinIdle(cloneDatasourceConfiguration.getMinPoolSize());
                        datasourceHandler.druidDataSource.setMaxActive(cloneDatasourceConfiguration.getMaxPoolSize());
                        if (cloneDatasourceConfiguration.getIdleTimeout() > 0) {
                            datasourceHandler.druidDataSource.setTimeBetweenEvictionRunsMillis(cloneDatasourceConfiguration.getIdleTimeout() * 60 * 1000);
                            datasourceHandler.druidDataSource.setMinEvictableIdleTimeMillis(cloneDatasourceConfiguration.getIdleTimeout() * 60 * 1000);
                        }
                        if (cloneDatasourceConfiguration.getBlockingTimeout() > 0) {
                            datasourceHandler.druidDataSource.setMaxWait(cloneDatasourceConfiguration.getBlockingTimeout());
                        }
                        log.info("[strict DRUID] flush ds success,dataId : " + dataId);
                        datasourceHandler.clearDataSourceWrapper();
                    } catch (Exception e) {
                        log.error("[strict DRUID] flush DataSource Error ! dataId:" + dataId + ",data:" + data, e);
                    }
                } else if (isRestrictChange) {
                    datasourceHandler.runTimeDatasourceConfiguration = cloneDatasourceConfiguration;
                    datasourceHandler.clearDataSourceWrapper();
                }
            }
        } catch (RuntimeException e1) {
            throw e1;
        } finally {
            datasourceHandler.lock.unlock();
        }
    }

    private boolean needReCreate(DatasourceConfiguration runTimeDatasourceConfiguration, DatasourceConfiguration newDatasourceConfiguration) {
        return !runTimeDatasourceConfiguration.getUsername().equals(newDatasourceConfiguration.getUsername());
    }

    private boolean changeNeedFlush(DatasourceConfiguration runTimeDatasourceConfiguration, DatasourceConfiguration newDatasourceConfiguration) {
        if (!runTimeDatasourceConfiguration.getConnectionProperties().equals(newDatasourceConfiguration.getConnectionProperties())) {
            return true;
        }
        if (!runTimeDatasourceConfiguration.getPassword().equals(newDatasourceConfiguration.getPassword())) {
            return true;
        }
        if (runTimeDatasourceConfiguration.getMinPoolSize() != newDatasourceConfiguration.getMinPoolSize()) {
            return true;
        }
        if (runTimeDatasourceConfiguration.getMaxPoolSize() != newDatasourceConfiguration.getMaxPoolSize()) {
            return true;
        }
        if (runTimeDatasourceConfiguration.getIdleTimeout() != newDatasourceConfiguration.getIdleTimeout()) {
            return true;
        }
        return runTimeDatasourceConfiguration.getBlockingTimeout() != newDatasourceConfiguration.getBlockingTimeout();
    }

    private boolean isRestrictChange(DatasourceConfiguration runTimeDatasourceConfiguration, DatasourceConfiguration newDatasourceConfiguration) {
        if (runTimeDatasourceConfiguration.getReadRestrictTimes() != newDatasourceConfiguration.getReadRestrictTimes()) {
            return true;
        }
        if (runTimeDatasourceConfiguration.getWriteRestrictTimes() != newDatasourceConfiguration.getWriteRestrictTimes()) {
            return true;
        }
        if (runTimeDatasourceConfiguration.getThreadCountRestrict() != newDatasourceConfiguration.getThreadCountRestrict()) {
            return true;
        }
        if (runTimeDatasourceConfiguration.getTimeSliceInMillis() != newDatasourceConfiguration.getTimeSliceInMillis()) {
            return true;
        }
        List<ConnectionRestrictEntry> runEntries = runTimeDatasourceConfiguration.getConnectionRestrictEntryList();
        List<ConnectionRestrictEntry> newEntries = newDatasourceConfiguration.getConnectionRestrictEntryList();
        if (runEntries != newEntries) {
            return runEntries == null || newEntries == null || !newEntries.equals(runEntries);
        }
        return false;
    }

}
